/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.rultax.neural.genetic;

import com.rultax.exception.NeuralNetworkError;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 *
 * @author Scott
 */
public abstract class GeneticAlgorithm<CHROMOSOME_TYPE extends Chromosome<?,?>> {
    private int populationSize;
    private double mutationPercent;
    private double percentToMate;
    private double matingPopulation;
    private boolean preventRepeat;
    private int cutLength;
    private ExecutorService pool;
    private CHROMOSOME_TYPE[] chromosomes;

    public CHROMOSOME_TYPE getChromosome(final int i){
        return chromosomes[i];
    }
    
    public CHROMOSOME_TYPE[] getChromosomes(){
        return chromosomes;
    }
    
    public int getCutLength(){
        return cutLength;
    }
    
    public double getMatingPopulation(){
        return this.matingPopulation;
    }
    
    public double getMutationPercent(){
        return mutationPercent;
    }
    
    public double getPercentToMate(){
        return percentToMate;
    }
    
    public ExecutorService getPool(){
        return pool;
    }
    
    public int getPopulationSize(){
        return populationSize;
    }
    
    public boolean isPreventRepeat(){
        return preventRepeat;
    }
    
    public void iteration() throws NeuralNetworkError{
        final int countToMate = (int)(getPopulationSize() * getPercentToMate());
        final int offspringCount = countToMate * 2;
        int offspringIndex = getPopulationSize() - offspringCount;
        final int matingPopulationSize = (int)(getPopulationSize() * getMatingPopulation());
        final Collection<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>();
        
        for (int i = 0; i < countToMate; i++) {
            final CHROMOSOME_TYPE mother = chromosomes[i];
            final int fatherInt = (int)(Math.random() * matingPopulationSize);
            final CHROMOSOME_TYPE father = chromosomes[fatherInt];
            final CHROMOSOME_TYPE child1 = chromosomes[offspringIndex];
            final CHROMOSOME_TYPE child2 = chromosomes[offspringIndex + 1];
            
            final MateWorker<CHROMOSOME_TYPE> worker = new MateWorker<CHROMOSOME_TYPE>(mother, father, child1, child2);
            
            try{
                if(pool != null)
                    tasks.add(worker);
                else
                    worker.call();
            }catch(final Exception e){
                
            }
            
            offspringIndex += 2;
        }
        
        if(pool != null){
            try{
                pool.invokeAll(tasks, 120, TimeUnit.SECONDS);
            }catch(final InterruptedException ex){
                
            }
        }
        
        sortChromosomes();        
    }
    
    public void setChromosome(final int i, final CHROMOSOME_TYPE value){
        chromosomes[i] = value;
    }
    
    public void setChromosomes(final CHROMOSOME_TYPE[] chromosomes){
        this.chromosomes = chromosomes;
    }
    
    public void setCutLength(final int cutLength){
        this.cutLength = cutLength;
    }
    
    public void setMatingPopulation(final double matingPopulation){
        this.matingPopulation = matingPopulation;
    }
    
    public void setMutationPercent(final double mutationPercent){
        this.mutationPercent = mutationPercent;
    }
    
    public void setPercentToMate(final double percentToMate){
        this.percentToMate = percentToMate;
    }
    
    public void setPool(final ExecutorService pool){
        this.pool = pool;
    }
    
    public void setPopulationSize(final int populationSize){
        this.populationSize = populationSize;
    }
    
    public void setPreventRepeat(final boolean preventRepeat){
        this.preventRepeat = preventRepeat;
    }
    
    public void sortChromosomes(){
        Arrays.sort(chromosomes);
    }
}
